package org.sanguine.study.hometasks.first;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;

import org.sanguine.study.hometasks.first.task.a.CommonStringTask;

import static java.lang.Math.*;

public class TasksFrom9to27 extends CommonStringTask {
	private String[] commandStringArguments;
	private MatrixOfDouble matrix;
 
	
	public static void main(String[] args) {
		Runner runner = new TasksFrom9to27();
		runner.run(args);
	}
	
	@Override
	protected void printUsage() {
		System.out.println("USAGE: java -jar TasksFrom9to27.jar 'arg1' 'arg2' 'arg3' ...");
	}

	@Override
	public boolean setUp(Reader in, PrintWriter out, String[] args) {
		if (super.setUp(in, out, args)) {
			commandStringArguments = args;
			return true;
		} else return false;
	}
	
	public void setMatrix(double[][] m) {
		if (m == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(m.length, m[0].length);
		matrix.setMatrix(m);
	}
	
	@Override
	public void doTask() {
		task9();
		task10();
		task11();
		task12();
		task13();
		task14();
		task15();
		task16();
		task17();
		task18();
		task19();
		task20();
		task21();
		task22();
		task23();
		task24();
		task25();
		task26();
		task27();
	}
	
	public void task10() {
		if (commandStringArguments == null || out == null) return;
		
		if (commandStringArguments.length < 4) {
			System.err.println("Please specify quadratic coefficients in command string.  'arg2' = a, 'arg3' = b, 'arg4' = c");
		} else {
			Double a, b, c;
			try {
				a = Double.parseDouble(commandStringArguments[1]);
				b = Double.parseDouble(commandStringArguments[2]);
				c = Double.parseDouble(commandStringArguments[3]);
			} catch (NumberFormatException e) {
				System.err.printf("Unknown numbers: a = %s, b = %s, c = %s%n", commandStringArguments[1], commandStringArguments[2], commandStringArguments[3]);
				return;
			}
			if (a == 0) {
				System.err.println("a must be non-zero");
				return;
			}
			
			Double D = b * b - 4.0 * a * c;
			
			Double x1, x2;
			Double ix1 = null, ix2 = null;
			
			if (D > 0) {
				x1 = (-b - sqrt(D)) / (2 * a);
				x2 = (-b + sqrt(D)) / (2 * a);
			} else if (D == 0) {
				x1 = x2 = -b / (2 * a);
			} else {
				x1 = x2 = -b / (2 * a);
				ix1 = -(sqrt(abs(D)) / (2 * a));
				ix2 = -ix1;
			}
			
			if (ix1 != null) {
				out.printf("Complex roots: z1 = %g - %g * i; z2 = %g + %g * i%n", x1, ix1, x2, ix2);
			} else {
				out.printf("Real roots: x1 = %g; x2 = %g%n", x1, x2);
			}
		}
 
	}

	public void task27() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		matrix.sortRowsByCharacteristicDescending().print(out);
	}

	public void task26() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		Double value = matrix.getTheBiggestLocalMaximum();
		if (value == null) out.println("The biggest local maximum not found");
		else out.printf("The biggest local maximum = %g%n", value);
	}

	public void task25() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		int count = matrix.countLocalMinimums();
		out.printf("Number of local minimums = %d%n", count);
	}

	public void task24() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		matrix.sortRowsBySumOfElements().print(out);
	}

	public void task23() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		int count = matrix.countSaddlePoints();
		out.printf("Number of saddle points = %d%n", count);
	}

	public void task22() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		matrix.roundValues().print(out);
	}

	public void task21() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		matrix.floatZeroValuesInRows().print(out);
	}

	public void task20() {
		if (numberOfItems == 0 || out == null) return;
		if (commandStringArguments.length < 3) {
			System.err.println("Please specify i,j as arguments");
			return;
		}
		
		int newRow = parseNumberAndCheck(commandStringArguments[1], 1, numberOfItems);
		int newColumn = parseNumberAndCheck(commandStringArguments[2], 1, numberOfItems);
		if (newRow == 0 || newColumn == 0) return;
		
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		newRow--;
		newColumn--;
		
		VectorValuesFinder[] minOfRows = new VectorValuesFinder[matrix.getNumberOfRows()];
		
		for (int i = 0; i < matrix.getNumberOfRows(); i++) minOfRows[i] = new VectorValuesFinder(matrix.getRow(i));
		
		double mostMin = minOfRows[0].getMinValue();
		int row = 0;
		
		//find min value of matrix
		for (int i = 1; i < minOfRows.length; i++)
			if (mostMin > minOfRows[i].getMinValue()) {
				row = i;
				mostMin = minOfRows[i].getMinValue();
			}
		
		matrix.swapRows(row, newRow).swapColumns(minOfRows[row].getIndexOfMin(), newColumn).print(out);
	}

	public void task19() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		for (int i = 0; i < matrix.getNumberOfRows(); i++) {
			if (matrix.isZeroRow(i)) matrix.deleteRow(i);
		}
		
		for (int i = 0; i < matrix.getRow(0).length; i++) {
			if (matrix.isZeroColumn(i)) matrix.deleteColumn(i);
		}

		matrix.print(out);
	}
	
	public void task18() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		VectorValuesFinder[] maxOfRows = new VectorValuesFinder[matrix.getNumberOfRows()];
		
		for (int i = 0; i < matrix.getNumberOfRows(); i++) maxOfRows[i] = new VectorValuesFinder(matrix.getRow(i));

		double mostMax = maxOfRows[0].getMaxValue();
		int row = 0;
		
		//find max value of matrix
		for (int i = 1; i < maxOfRows.length; i++)
			if (mostMax < maxOfRows[i].getMaxValue()) {
				row = i;
				mostMax = maxOfRows[i].getMaxValue();
			}
		
		//delete rows and columns containing max value 
		for (int i = maxOfRows.length - 1; i >= row; i--) {
			if (mostMax == maxOfRows[i].getMaxValue()) {
				matrix.deleteRow(i);
				int column = maxOfRows[i].getIndexOfMax();
				int deleted = 0;
				do {
					matrix.deleteColumn(column - deleted++);
					column = maxOfRows[i].getNextIndexOfMax();
				} while (column >= 0);
			}
		}
		
		matrix.print(out);
	}

	public void task17() {
		if (numberOfItems == 0 || out == null) return;
		SquareMatrixOfDouble matrix = new SquareMatrixOfDouble(numberOfItems);
		matrix.createRandomMatrix(numberOfItems);

		Double determinant = matrix.getDeterminant();
		if (determinant == null) out.println("Something is wrong, determinant is not available");
		else out.printf("Determinant = %g%n", determinant);
	}

	public void task16() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		matrix.rotateCounterclockwise().print(out);
	}

	public void task15() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);

		double sum = 0.0;
		
		for (int i=0; i < matrix.getNumberOfRows(); i++) {
			sum += matrix.calculateSumBetweenPositiveNumbersInRow(i);
		}
		
		out.printf("Sum = %g%n", sum);
	}

	public void task14() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		matrix.findAscendingRange().printRange(out, matrix.getRangeBegin(), matrix.getRangeLength());
	}

	public void task13() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		
		int k = parseNumberAndCheck(commandStringArguments[1], 1, numberOfItems - 1);
		if (k != 0)	matrix.cyclicRowsDownShift(k).print(out);
	}

	public void task12() {
		if (numberOfItems == 0 || out == null) return;
		if (matrix == null) matrix = new MatrixOfDouble(numberOfItems, numberOfItems).createRandomMatrix(numberOfItems);
		if (commandStringArguments == null || commandStringArguments.length < 2) {
			System.err.println("Please specify at least two arguments in command line");
			return;
		}
		int k = parseNumberAndCheck(commandStringArguments[1], 1, numberOfItems);
		if (k != 0)	matrix.sortRowsByColumnValues(k - 1).print(out);
	}

	private int parseNumberAndCheck(String inputString, int lowest, int highest) {
		int k;
		try {
			k = Integer.parseInt(inputString); 
		} catch (NumberFormatException e) {
			System.err.println("Wrong number format: " + inputString);
			return lowest - 1;
		}
		if (k < lowest || k > highest) {
			System.err.printf("number must be from %d to %d%n", lowest, highest);
			return lowest - 1;
		} else return k;
	}

	public void task11() {
		if (in == null || out == null) return;
		BufferedReader reader = new BufferedReader(in);
		String inString;
		try {
			inString = reader.readLine();
		} catch (IOException e) {
			System.err.println("IO Error");
			e.printStackTrace();
			return;
		}
		if (inString != null) {
			int month = parseNumberAndCheck(inString, 1, 12);
			
			switch (month) {
				case 1:
					out.println("January");
					break;
				case 2:
					out.println("February");
					break;
				case 3:
					out.println("March");
					break;
				case 4:
					out.println("April");
					break;
				case 5:
					out.println("May");
					break;
				case 6:
					out.println("June");
					break;
				case 7:
					out.println("July");
					break;
				case 8:
					out.println("August");
					break;
				case 9:
					out.println("September");
					break;
				case 10:
					out.println("October");
					break;
				case 11:
					out.println("November");
					break;
				case 12:
					out.println("December");
					break;
				default:
					out.println("Please input number from 1 to 12");
					break;
			}
		} else {
			System.err.println("Unexpected end of stream");
		}
	}

	public void task9() {
		if (out == null) return;
		
		for (int i = 1; i <= 25; i++) {
			out.printf("%d ", i);
			if (i % 5 == 0) out.println();
		}
	}

}
